package com.zz.utils;

import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zz.common.ZohoRecord;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author HuaiJinYuQ
 * @apiNote
 * @since 2022/9/8 15:03 创建
 */


@Slf4j
@UtilityClass
public class HttpUtil {
    RestTemplate client = new RestTemplate();
    ZohoRecord zohoRecord = new ZohoRecord();

    public JSONObject postData(String url, Map<String, Object> requestMap) {
        HttpHeaders headers = new HttpHeaders();

        // 以表单的方式提交
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 将请求头部和参数合成一个请求
        HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(requestMap), headers);
        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.POST, requestEntity, String.class);

        return JSONObject.parseObject(result.getBody());
    }

    public JSONObject postJsonData(String url, String json) {
        HttpHeaders headers = new HttpHeaders();


        // 以表单的方式提交
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 将请求头部和参数合成一个请求
        HttpEntity<String> requestEntity = new HttpEntity<>(json, headers);
        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.POST, requestEntity, String.class);

        return JSONObject.parseObject(result.getBody());
    }

    public JSONObject postDataByHead(String token, String url, String json) {
        HttpHeaders headers = new HttpHeaders();

        // 以表单的方式提交
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add("Authorization", "Zoho-oauthtoken " + token);
        // 将请求头部和参数合成一个请求
        HttpEntity<String> requestEntity = new HttpEntity<>(json, headers);
        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.POST, requestEntity, String.class);

        return JSONObject.parseObject(result.getBody());
    }

    public JSONObject getData(String url, Map<String, String> headerMap) {

        HttpHeaders headers = new HttpHeaders();
        if (!CollectionUtils.isEmpty(headerMap)) {
            headerMap.forEach(headers::add);
        }
        // 将请求头部和参数合成一个请求
        HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(headers);

        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.GET, requestEntity, String.class);
        return JSONObject.parseObject(result.getBody());
    }

    public InputStream downloadAttachmentData(String url, Map<String, String> headerMap) {

        HttpHeaders headers = new HttpHeaders();
        if (!CollectionUtils.isEmpty(headerMap)) {
            headerMap.forEach(headers::add);
        }
        // 将请求头部和参数合成一个请求
        HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(headers);

        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<Resource> exchange = client.exchange(url, HttpMethod.GET, requestEntity, Resource.class);
        try {
            return Objects.requireNonNull(exchange.getBody()).getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 返回文件流
    public InputStream getFileData(String url, Map<String, String> headerMap) {

        HttpHeaders headers = new HttpHeaders();
        if (!CollectionUtils.isEmpty(headerMap)) {
            headerMap.forEach(headers::add);
        }
        // 将请求头部和参数合成一个请求
        HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(headers);

        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<Resource> exchange = client.exchange(url, HttpMethod.GET, requestEntity, Resource.class);
        try {
            if (Objects.nonNull(exchange.getBody())) {
                return exchange.getBody().getInputStream();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public JSONObject putData(String url, Map<String, Object> requestMap) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf("application/x-www-form-urlencoded;charset=UTF-8"));
        headers.add("Authorization", "Zoho-oauthtoken " + zohoRecord.getAccessToken());
        // 将请求头部和参数合成一个请求
        HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(requestMap), headers);
        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.PUT, requestEntity, String.class);

        return JSONObject.parseObject(result.getBody());
    }

    public JSONObject putDataByHead(String token, String url, Map<String, Object> requestMap) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf("application/x-www-form-urlencoded;charset=UTF-8"));
        headers.add("Authorization", "Zoho-oauthtoken " + token);
        // 将请求头部和参数合成一个请求
        HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(requestMap), headers);
        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.PUT, requestEntity, String.class);

        return JSONObject.parseObject(result.getBody());
    }

    /**
     * 通过Stream方式请求文件接口
     */
    public JSONObject postFileByStream(String accessToken, String filePath, String serverUrl) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.setConnection("Keep-Alive");
        headers.setCacheControl("no-cache");
        // 以multipart/form-data的方式提交
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.add("Authorization", "Zoho-oauthtoken " + accessToken);
        FileSystemResource resource = new FileSystemResource(new File(filePath));
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("file", resource);
        HttpEntity<Object> httpEntity = new HttpEntity<>(map, headers);
        ResponseEntity<String> result = client.postForEntity(serverUrl, httpEntity, String.class);
        return JSONObject.parseObject(result.getBody());
    }

    /**
     * 通过Stream方式请求文件接口
     */
    public JSONObject postFileByFileStream(String accessToken, String filePath, String serverUrl) {
        HttpHeaders headers = new HttpHeaders();
        headers.setConnection("Keep-Alive");
        headers.setCacheControl("no-cache");
        // 以multipart/form-data的方式提交
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.add("Authorization", "Zoho-oauthtoken " + accessToken);
        FileSystemResource resource = new FileSystemResource(new File(filePath));
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("file", resource);
        HttpEntity<Object> httpEntity = new HttpEntity<>(map, headers);
        ResponseEntity<String> result = client.postForEntity(serverUrl, httpEntity, String.class);
        return JSONObject.parseObject(result.getBody());
    }

    // COQLQuery接口
    public JSONObject postDataByCOQLQuery(String token, String url, Map<String, Object> requestMap) {
        HttpHeaders headers = new HttpHeaders();

        // 以表单的方式提交
        headers.add("Authorization", "Zoho-oauthtoken " + token);

        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(requestMap), headers);
        // 执行HTTP请求，将返回的结构使用JSONObject类格式化
        ResponseEntity<String> result = client.exchange(url, HttpMethod.POST, requestEntity, String.class);

        return JSONObject.parseObject(result.getBody());
    }

    // bulk
    public List<CsvRow> getBulkCSVData(String token, String destFile, String query,List<CsvRow>  result) {

        // 1 Create Bulk Read job
        String jobId = createBulkJob(token, query);
        if (StringUtils.isNotBlank(jobId)) {
            // 2 Get the Status of the Bulk Read Job
            log.info("Get the Status of the Bulk Read Job");
            boolean boo = GetStatusBulkJob(token, jobId);

            // 3 Download Bulk Read Result
            List<CsvRow> csvRows = downBulkResult(token, destFile, jobId);
            if (Objects.nonNull(csvRows)) {
                result.addAll(csvRows);
            }

            if (boo) {
                JSONObject queryJson = JSONObject.parseObject(query);
                JSONObject tempQuery = queryJson.getJSONObject("query");
                tempQuery.put("page", tempQuery.getInteger("page") + 1);
                queryJson.put("query", tempQuery);

                getBulkCSVData(token, destFile, queryJson.toJSONString(),result);
            }

        }
        return result;
    }

    public String createBulkJob(String token, String query) {
        log.info("Create Bulk Read job");
        String url = "https://www.zohoapis.com.cn/crm/bulk/v3/read";
        JSONObject createData = HttpUtil.postDataByHead(token, url, query);
        if (Objects.nonNull(createData) && Objects.nonNull(createData.getJSONArray("data")) && createData.getJSONArray("data").size() > 0) {
            JSONObject json = createData.getJSONArray("data").getJSONObject(0);
            if ("success".equals(json.getString("status"))) {
                return json.getJSONObject("details").getString("id");
            }
        }
        log.info("Create Bulk Read job error = " + createData.toJSONString());
        return "";
    }

    public boolean GetStatusBulkJob(String token, String jobId) {

        String url = "https://www.zohoapis.com.cn/crm/bulk/v3/read/" + jobId;
        HashMap<String, String> map = new HashMap<>();
        map.put("Authorization", "Zoho-oauthtoken " + token);

        boolean moreRecords;

        while (true) {
            JSONObject data = HttpUtil.getData(url, map);
            if (Objects.nonNull(data) && Objects.nonNull(data.getJSONArray("data")) && data.getJSONArray("data").size() > 0) {
                JSONObject jsonData = data.getJSONArray("data").getJSONObject(0);
                String state = jsonData.getString("state");
                System.out.println("state = " + state);
                if ("COMPLETED".equals(state)) {
                    moreRecords = jsonData.getJSONObject("result").getBoolean("more_records");
                    break;
                }
                ThreadUtil.safeSleep(10000);

            } else {
                log.info("Get the Status of the Bulk Read Job error = " + data.toJSONString());
                return false;
            }
        }
        return moreRecords;
    }

    public List<CsvRow> downBulkResult(String token, String destFile, String jobId) {
        log.info("Download Bulk Read Result");
        File zipFile = new File(destFile + jobId + ".zip");
        HttpRequest.get("https://www.zohoapis.com.cn/crm/bulk/v3/read/" + jobId + "/result")
                .header(Header.AUTHORIZATION, "Zoho-oauthtoken " + token)
                .executeAsync().writeBody(zipFile);

        if (zipFile.exists()) {
            ZipUtil.unzip(zipFile);
            CsvReader reader = CsvUtil.getReader();
            CsvData read = reader.read(new File(destFile + jobId + "/" + jobId + ".csv"));
            System.out.println("本次共计：" + read.getRowCount() + "条数据");
            return read.getRows();
        }
        return null;
    }
}